//Copyright 2012-2019 Keysight Technologies
//
//Licensed under the Apache License, Version 2.0 (the "License");
//you may not use this file except in compliance with the License.
//You may obtain a copy of the License at
//
//http://www.apache.org/licenses/LICENSE-2.0
//
//Unless required by applicable law or agreed to in writing, software
//distributed under the License is distributed on an "AS IS" BASIS,
//WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
//See the License for the specific language governing permissions and
//limitations under the License.
using System;
using System.Collections;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Runtime.CompilerServices;
using OpenTap;
namespace PluginDevelopment.Advanced_Examples
{
// This file shows an example of how to build an object that has more than usually complex data
// and make it editable and serializable in a normal way.
// There are more ways than this, but this is probably the simplest ways of making it.
/// This settings element has some rather complex behaviors. For example it needs to know which DUT instance
/// owns it.
public class ComplexSettingsElement1 : INotifyPropertyChanged
{
///
/// In order to have the right AvailableInts, we need the DUT value to always be up to date.
///
public IEnumerable AvailableInts => Dut?.AvailableInts ?? Enumerable.Empty();
int a;
[AvailableValues(nameof(AvailableInts))]
public int A
{
get => a;
set
{
if (a == value) return;
a = value;
OnPropertyChanged();
}
}
int b;
[AvailableValues(nameof(AvailableInts))]
public int B
{
get => b;
set
{
if (b == value) return;
b = value;
OnPropertyChanged();
}
}
internal ComplexSettingsExample1 Dut;
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
[Display("Complex Settings DUT1", "Demonstrates how to use a list where items cannot be added or removed." +
" Elements themselves can be changed, but they are also mostly read-only.",
Groups: new[] { "Examples", "Plugin Development", "Advanced Examples" })]
public class ComplexSettingsExample1 : Dut
{
public List AvailableInts { get; set; }
NotifyList elements = new NotifyList();
public NotifyList Elements
{
get
{
elementsNotifyChanged(elements);
elements.NotifyChanged = elementsNotifyChanged;
elements.ElementChanged = onElementChanged;
return elements;
}
set => elements = (value as NotifyList) ?? new NotifyList(value);
}
void onElementChanged(NotifyList list, ComplexSettingsElement1 element1)
{
// insert logic to handle changes in the element
Log.Debug("Element {0} changed", list.IndexOf(element1) + 1);
}
void elementsNotifyChanged(NotifyList list)
{
foreach (var elem in list)
elem.Dut = this;
}
}
/// List type that is easily extendible.
public class VirtualList : IList, IList
{
List elements = new List();
public virtual IEnumerator GetEnumerator() => elements.GetEnumerator();
IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
public void Add(T item) => Insert(Count, item);
public int Add(object value)
{
Add((T) value);
return Count - 1;
}
public virtual bool Contains(object value) => ((IList)elements).Contains(value);
public virtual void Clear() => elements.Clear();
public int IndexOf(object value) => IndexOf((T) value);
public void Insert(int index, object value) => Insert(index, (T) value);
public void Remove(object value) => Remove((T) value);
public virtual bool Contains(T item) => elements.Contains(item);
public virtual void CopyTo(T[] array, int arrayIndex) => elements.CopyTo(array, arrayIndex);
public bool Remove(T item)
{
var idx = IndexOf(item);
if (idx == -1) return false;
RemoveAt(idx);
return true;
}
public void CopyTo(Array array, int index) => CopyTo((T[]) array, index);
public virtual int Count => elements.Count;
public object SyncRoot => false;
public bool IsSynchronized => false;
public virtual bool IsReadOnly => ((IList) elements).IsReadOnly;
public bool IsFixedSize => false;
public virtual int IndexOf(T item) => elements.IndexOf(item);
public virtual void Insert(int index, T item) => elements.Insert(index, item);
public virtual void RemoveAt(int index) => elements.RemoveAt(index);
object IList.this[int index]
{
get => this[index];
set => this[index] = (T) value;
}
public virtual T this[int index]
{
get => elements[index];
set => elements[index] = value;
}
}
/// A type of list that can emit notifications on change.
public class NotifyList : VirtualList
{
public NotifyList(IEnumerable initialElements = null)
{
foreach (var elem in initialElements ?? Enumerable.Empty())
Add(elem);
}
public NotifyList()
{
}
// Invoked when the list has changed.
public Action> NotifyChanged { get; set; }
/// Invoked when an element of the list has changed.
public Action, T> ElementChanged { get; set; }
bool notify = true;
void notifyOnChanged()
{
if(notify)
NotifyChanged?.Invoke(this);
}
public override void Insert(int index, T item)
{
base.Insert(index, item);
if (item is INotifyPropertyChanged n)
n.PropertyChanged += elementPropertyChanged;
notifyOnChanged();
}
void elementPropertyChanged(object sender, PropertyChangedEventArgs e) => ElementChanged?.Invoke(this, (T)sender);
public override T this[int index]
{
get => base[index];
set
{
notify = false;
try
{
RemoveAt(index);
}
finally
{
notify = true;
}
Insert(index, value);
}
}
public override void Clear()
{
base.Clear();
foreach (var item in this)
{
if (item is INotifyPropertyChanged n)
n.PropertyChanged -= elementPropertyChanged;
}
notifyOnChanged();
}
public override void RemoveAt(int index)
{
if (index >= Count) return;
var elem = this[index];
if (elem is INotifyPropertyChanged n)
n.PropertyChanged -= elementPropertyChanged;
base.RemoveAt(index);
notifyOnChanged();
}
}
}